package procsim;

import java.awt.*;
import java.util.*;
import java.awt.image.*;
import java.text.*;
import java.awt.font.*;

public class GMSignal extends MSignal implements GraphicalLine {

    private LinkedHashMap<Coords, ImageObserver> coords;
    private boolean displayName = false;
    private int spacing = 0;

    public GMSignal(int numOfBits, int value) {
        super(numOfBits, value);
        coords = new LinkedHashMap<Coords, ImageObserver>();
    }

    public GMSignal(int numOfBits, int value, boolean display) {
        super(numOfBits, value);
        coords = new LinkedHashMap<Coords, ImageObserver>();
        displayName = display;
    }

    public GMSignal(int numOfBits, int value, String name) {
        super(numOfBits, value, name);
        coords = new LinkedHashMap<Coords, ImageObserver>();
    }

    public GMSignal(int numOfBits, int value, String name, boolean display) {
        super(numOfBits, value, name);
        coords = new LinkedHashMap<Coords, ImageObserver>();
        displayName = display;
    }

    public Color getColor(){
        if(get() == 0) return Color.BLUE;
        else if (getNumOfBits()>1) return Color.GREEN;
        else return Color.RED;
    }

    public GMSignal setSpacing(int spacing){
        this.spacing = spacing;
        return this;
    }

    @Override
    public GMSignal addCoords(ImageObserver observer, Coords coor){
        coords.put(coor, observer);
        return this;
    }

    @Override
    public GMSignal addCoords(ImageObserver observer, int x1, int y1, int x2, int y2){
        coords.put(new Coords(x1, y1, x2, y2), observer);
        return this;
    }

    @Override
    public void drawIt(Graphics2D g, ImageObserver observer) {
        Util.saveGraphics(g);
        ArrayList<Coords> prevcoords = new ArrayList<Coords>();
        if(spacing > 0){
            int[] signals = new int[getBits().length];
            int j = getBits().length-1;
            int m = 0;
            while(j!=-1){
                signals[m] = getBits()[j].get();
                m++;
                j--;
            }
            boolean first = true;
            GSignal prevx = new GSignal(0);
            for(int i = 0; i < getBits().length; i++){
                GSignal gsig = new GSignal(signals[i]);
                if(first){
                    Iterator iter = coords.keySet().iterator();
                    while(iter.hasNext()){
                        Coords temp = (Coords)iter.next();
                        ImageObserver tmp = coords.get(temp);
                        if(tmp!=observer) continue;
                        gsig.addCoords(observer, temp);
                        prevx = gsig;
                        first = false;
                    }
                }
                else{
                    gsig.setCoordsRelativeTo(observer, prevx, spacing);
                    prevx = gsig;
                }
                gsig.drawIt(g, observer);
            } // end main for
        } // end if spacing > 0
        else {
            Iterator iter = coords.keySet().iterator();
            while(iter.hasNext()) {
                Coords drawing = (Coords)iter.next();
                ImageObserver tmp = coords.get(drawing);
                if(tmp!=observer) continue;
                BasicStroke stroke = getStroke();
                // iznad prvog dela crte se ispisuje vrednost
                g.setColor(new Color(70, 70, 70));
                g.setFont(new Font("Arial", Font.BOLD, 12));
                if(drawing.getX1() > drawing.getX2() || drawing.getY1() > drawing.getY2()){
                    if(drawing.getX1() - drawing.getX2() > drawing.getY1() - drawing.getY2()){
                         if(writeName(drawing, prevcoords)){
                            if(displayName){
                                g.drawString(Util.int2Hex(get(), getNumOfBits()), drawing.getX1()-32, drawing.getY1()+11);
                                g.drawString(getAttributedName().getIterator(), drawing.getX1()-32, drawing.getY1()-2);
                            }
                            else
                                g.drawString(Util.int2Hex(get(), getNumOfBits()), drawing.getX1()-32, drawing.getY1()-2);
                         }
                    }
                    else {
                        if(writeName(drawing, prevcoords)){
                            if(displayName){
                                g.drawString(Util.int2Hex(get(), getNumOfBits()), drawing.getX1()+2, drawing.getY1()-12);
                                g.drawString(getAttributedName().getIterator(), drawing.getX1()+2, drawing.getY1()-25);
                            }
                            else
                                g.drawString(Util.int2Hex(get(), getNumOfBits()), drawing.getX1()+2, drawing.getY1()-25);
                        }
                    }
                }
                else {
                    if(drawing.getX2() - drawing.getX1() > drawing.getY2() - drawing.getY1()){
                        if(writeName(drawing, prevcoords)){
                            if(displayName){
                                g.drawString(Util.int2Hex(get(), getNumOfBits()), drawing.getX1()+2, drawing.getY1()+11);
                                g.drawString(getAttributedName().getIterator(), drawing.getX1()+2, drawing.getY1()-3);
                            } else
                                g.drawString(Util.int2Hex(get(), getNumOfBits()), drawing.getX1()+2, drawing.getY1()-3);
                        }
                    }
                    else {
                        if(writeName(drawing, prevcoords)){
                            if(displayName){
                                g.drawString(Util.int2Hex(get(), getNumOfBits()), drawing.getX1()+2, drawing.getY1()+25);
                                g.drawString(getAttributedName().getIterator(), drawing.getX1()+2, drawing.getY1()+12);
                            } else
                                g.drawString(Util.int2Hex(get(), getNumOfBits()), drawing.getX1()+2, drawing.getY1()+12);
                        }
                    }
                }
                prevcoords.add(drawing);
                g.setColor(getColor());
                g.setStroke(stroke);
                g.drawLine(drawing.getX1(), drawing.getY1(), drawing.getX2(), drawing.getY2());
            }
        }
        Util.restoreGraphics(g);
    }

    private boolean writeName(Coords drawing, ArrayList<Coords> all){
        Iterator iter = all.iterator();
        while(iter.hasNext()) {
            Coords cur = (Coords)iter.next();
            if(cur.getX1()==cur.getX2() &&
                drawing.getY1()==drawing.getY2() &&
                (drawing.getX1()==cur.getX1() || drawing.getX2()==cur.getX2()) &&
                ((cur.getY1()>cur.getY2() && ((cur.getY1()>=drawing.getY1() && cur.getY2()<=drawing.getY1()))) ||
                (cur.getY1()<cur.getY2() && ((cur.getY1()<=drawing.getY1() && cur.getY2()>=drawing.getY1())))))
                return false;
            if(cur.getY1()==cur.getY2() &&
                drawing.getX1()==drawing.getX2() &&
                (drawing.getY1()==cur.getY1() || drawing.getY2()==cur.getY2()) &&
                ((cur.getX1()>cur.getX2() && ((cur.getX1()>=drawing.getX1() && cur.getX2()<=drawing.getX1()))) ||
                (cur.getX1()<cur.getX2() && ((cur.getX1()<=drawing.getX1() && cur.getX2()>=drawing.getX1())))))
                return false;
        }
        return true;
    }

    private BasicStroke getStroke(){
        int width;
        if(getNumOfBits()==1) width = 1;
        else width = 2;
        return new BasicStroke(width);
    }

    private AttributedString getAttributedName(){
        AttributedString attName = new AttributedString(getName());
        int start = 0, end = 0;
        if(getName().contains("..")){
            char[] temp = new char[getName().length()];
            temp = getName().toCharArray();
            for(int i = 0; i < temp.length; i++){
                if(Character.isDigit(temp[i])){
                    start = i;
                    break;
                }
            }
            end = getName().length();
            attName.addAttribute(TextAttribute.SIZE, 9, start, end);
        }
        return attName;
    }
}
